//
//  NetworkTools.swift
//  GZWeibo
//
//  Created by Apple on 15/9/22.
//  Copyright © 2015年 itcast. All rights reserved.
//

import UIKit
import AFNetworking

/// 请求方法的枚举
enum RequestMethod: String {
    case GET = "GET"
    case POST = "POST"
}

/// 网络工具单例
class NetworkTools: AFHTTPSessionManager {

    // OC 的 typedefine 非常类似，定义数据类型
    // 网络请求完成回调的数据类型 - 首字母要大写
    typealias RequestFinishedCallBack = (result: AnyObject?, error: NSError?) -> ()
    
    /// 网络工具单例
    static let sharedTools: NetworkTools = {
        let instance = NetworkTools(baseURL: nil)
        
        // 设置反序列化格式 - Xcode 会自动将 OC 代码中的 NSSet 转换成 Set 类型
        instance.responseSerializer.acceptableContentTypes?.insert("text/plain")
        
        return instance
    }()
    
    // MARK: - 应用程序信息
    private let appKey = "3995771732"
    private let appSecret = "c7b253a8381da34eb612a190fee5d100"
    /// 授权回调地址
    let redirectURI = "http://www.baidu.com"
    
    // MARK: - 创建 token 字典
    /// 目前有很多公司，都会封装自己的网络访问方法，会把用户身份安全的代码全部封装！
    private var tokenParams: [String: AnyObject]? {
        // 1. 判断用户账户中是否存在token，如果没有存在说明用户没有登录，或者 token 过期
        guard let token = UserAccountViewModel.sharedAccountViewModel.userAccount?.access_token else {
            // token 不存在
            return nil
        }
        
        // 2. 返回 token 字典
        return ["access_token": token]
    }
    
    // MARK: - 发布微博
    /// 发布微博
    ///
    /// - parameter status:   微博正文
    /// - parameter image:    上传的图片，如果没有就是文本微博
    /// - parameter finished: 完成回调
    /// - see: [文本微博](http://open.weibo.com/wiki/2/statuses/update)
    func postStatus(status: String, image: UIImage?, finished: RequestFinishedCallBack) {
        // 1. 建立 token 字典
        guard var params = tokenParams else {
            // 如果 token 为 nil，需要通知控制器，不再执行网络方法
            finished(result: nil, error: NSError(domain: "cn.itcast.error", code: -1002, userInfo: ["message": "token 为 nil"]))
            return
        }
        
        // 2. 设置微博文本
        params["status"] = status
        
        // 3. 发起网络请求
        // 判断是否有图片
        if let img = image,
            data = UIImagePNGRepresentation(img) {  // 图片微博
                
                upload("https://upload.api.weibo.com/2/statuses/upload.json", data: data, parameters: params, finished: finished)
                
        } else {        // 文本微博
            request(.POST, urlString: "https://api.weibo.com/2/statuses/update.json", parameters: params, finished: finished)
        }
    }
    
    // MARK: - 加载微博数据
    /// 加载微博数据
    ///
    /// - parameter since_id:   若指定此参数，则返回ID比since_id大的微博（即比since_id时间晚的微博），默认为0
    /// - parameter max_id:     若指定此参数，则返回ID小于或等于max_id的微博，默认为0
    /// - parameter finished:   完成回调
    /// - see: [http://open.weibo.com/wiki/2/statuses/home_timeline](http://open.weibo.com/wiki/2/statuses/home_timeline)
    func loadStatus(since_id since_id: Int, max_id: Int, finished: RequestFinishedCallBack) {
        // 1. 建立 token 字典
        guard var params = tokenParams else {
            // 如果 token 为 nil，需要通知控制器，不再执行网络方法
            finished(result: nil, error: NSError(domain: "cn.itcast.error", code: -1002, userInfo: ["message": "token 为 nil"]))
            return
        }
        
        // 2. 处理刷新参数
        if since_id > 0 {       // 下拉刷新
            params["since_id"] = since_id
        } else if max_id > 0 {  // 上拉刷新
            params["max_id"] = max_id - 1
        }
        
        // 3. 发起网络请求
        request(.GET, urlString: "https://api.weibo.com/2/statuses/home_timeline.json", parameters: params, finished: finished)
    }
    
    // MARK: - 用户信息
    /// 加载用户
    ///
    /// - parameter uid:         uid
    /// - parameter finished:    完成回调
    /// - see: [http://open.weibo.com/wiki/2/users/show](http://open.weibo.com/wiki/2/users/show)
    func loadUserInfo(uid: String, finished: RequestFinishedCallBack) {
        
        // 1. 建立 token 字典
        guard var params = tokenParams else {
            // 如果 token 为 nil，需要通知控制器，不再执行网络方法
            finished(result: nil, error: NSError(domain: "cn.itcast.error", code: -1002, userInfo: ["message": "token 为 nil"]))
            return
        }
        
        // 2. 向字典中增加参数
        params["uid"] = uid
        
        // 2. 发起网络请求
        request(.GET, urlString: "https://api.weibo.com/2/users/show.json", parameters: params, finished: finished)
    }
    
    // MARK: - 身份验证
    /// OAuth 登录 URL 地址
    /// - see: [http://open.weibo.com/wiki/Oauth2/authorize](http://open.weibo.com/wiki/Oauth2/authorize)
    /// 计算型属性，类似于函数，必须有返回值，同时不能有参数，使用频率，使用属性，代码的语义会更清晰
    var oauthURL: NSURL {
        let urlString = "https://api.weibo.com/oauth2/authorize?client_id=\(appKey)&redirect_uri=\(redirectURI)"
        
        return NSURL(string: urlString)!
    }
    
    /// 使用 code 加载 token
    ///
    /// - parameter code:     code
    /// - parameter finished: 完成回调
    func loadAccessToken(code: String, finished: RequestFinishedCallBack) {
        let params = ["client_id": appKey,
            "client_secret": appSecret,
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": redirectURI]
        
        request(.POST, urlString: "https://api.weibo.com/oauth2/access_token", parameters: params, finished: finished)
        
        // 测试代码
//        // 1. 设置响应数据格式为二进制数据 AFN 不会做 JSON 反序列化
//        responseSerializer = AFHTTPResponseSerializer()
//        
//        // 2. 测试网络代码 － Swift 开发中，凡是碰到 AnyObject 要想使用，就必须转换！
//        request(.POST, urlString: "https://api.weibo.com/oauth2/access_token", parameters: params) { (result, error) -> () in
//            // result 是一个 NSData
//            // 将数据转换成 NSString
//            let string = NSString(data: result as! NSData, encoding: NSUTF8StringEncoding)
//            
//            // {"access_token":"2.00ml8IrFKlq63E7ccdbba6c5OotBTD","remind_in":"157679999","expires_in":157679999,"uid":"5365823342"}
//            // 提示：JSON 反序列化的时候，如果数字没有引号，转换成 NSNumber，可能会导致 KVC 的崩溃！
//            print(string)
//        }
    }
    
    // MARK: - 封装 AFN 网络访问方法
    /// 发起网络请求
    ///
    /// - parameter method:     method GET / POST
    /// - parameter urlString:  urlString
    /// - parameter parameters: 参数字典
    /// - parameter finished:   完成回调
    private func request(method: RequestMethod, urlString: String, parameters: [String: AnyObject]?, finished: RequestFinishedCallBack) {
        
        // 闭包是提前准备好的代码，可以当作参数传递
        let success = { (task: NSURLSessionDataTask, result: AnyObject) -> () in
            finished(result: result, error: nil)
        }
        let failure = { (task: NSURLSessionDataTask, error: NSError) -> () in
            print(error)
            finished(result: nil, error: error)
        }
        
        if method == RequestMethod.GET {
            GET(urlString, parameters: parameters, success: success, failure: failure)
        } else {
            POST(urlString, parameters: parameters, success: success, failure: failure)
        }
    }
    
    /// 上传文件
    ///
    /// - parameter urlString:  urlString
    /// - parameter data:       要上传文件的二进制数据
    /// - parameter parameters: 参数字典
    /// - parameter finished:   完成回调
    private func upload(urlString: String, data: NSData, parameters: [String: AnyObject]?, finished: RequestFinishedCallBack) {
        
        POST(urlString, parameters: parameters, constructingBodyWithBlock: { (formData) -> Void in
            
            /**
                1. 要上传文件的二进制数据
                2. `服务器提供的`接口文件名称
                3. 保存在服务器的文件名，通常可以乱写
                4. mimeType: 客户端高速服务器，上传图片的类型
                   contentType: 服务器高速客户端数据的类型
            
                    格式：大类型/小类型
                    如果不想告诉服务器文件类型，可以使用 application/octet-stream
            
                如果开发中碰到上传多个文件，可以用一个 for 顺序添加
            */
            formData.appendPartWithFileData(data, name: "pic", fileName: "xxx", mimeType: "application/octet-stream")
            
            }, success: { (_, result) -> Void in
                finished(result: result, error: nil)
            }) { (_, error) -> Void in
                
                print(error)
                finished(result: nil, error: error)
        }
    }
}
